add support for multiple urls for route headers. (#276)
authortsteven4 <tsteven4@users.noreply.github.com>
Thu, 20 Dec 2018 16:38:22 +0000 (09:38 -0700)
committerGitHub <noreply@github.com>
Thu, 20 Dec 2018 16:38:22 +0000 (09:38 -0700)
* add support for multiple urls for route headers.

add support in gpx for gpx/rte/link, gpx/rte/url, gpx/rte/urlname.
add support in gpx for gpx/trk/link, gpx/trk/url, gpx/trk/urlname.

Note that the gpx writer can violate the schema when fprint_xml_chain
is used to echo unrecognized elements from input to output.
This is because the gpx 1.0/1.1 schema requires an xsd:sequence of
elements, i.e. the order of the elements is specified, and any
elements output by fprint_xml_chain won't necessarily be in
the correct order with other elements that are specifically
handled in the gpx writer.

By processing additional gpx elements as listed above on read we
prevent them from causing schema violations on write.  However,
other unrecognized elements can still cause schema violations.

* make common link code common in gpx reader.

cst.cc
defs.h
garmin_txt.cc
gdb.cc
gpx.cc
pocketfms_bc.cc
random.cc
reference/pocketfms_bc.gpx
route.cc
waypt.cc

diff --git a/cst.cc b/cst.cc
index 5dd4ba52ca1e8c6ba9e79bff7dd8b2eb5b714dff..222bef1d02d28964a93f4017f9f4e1d896e8b8a0 100644 (file)
--- a/cst.cc
+++ b/cst.cc
@@ -61,7 +61,7 @@ cst_add_wpt(route_head* track, Waypoint* wpt)
     // Rather than creating a new waypt on each read, tis format bizarrely
     // recycles the same one, relying on new waypoint(*) above and then manually
     // resetting fields.  Weird.
-    wpt->url_link_list_.clear();
+    wpt->urls.clear();
 
     if (temp_route == nullptr) {
       temp_route = route_head_alloc();
diff --git a/defs.h b/defs.h
index b0df8e74357ffe0570b7c261fc7f881cb4483687..af26f805437641dd283c7f36fb80bc5acb6e285b 100644 (file)
--- a/defs.h
+++ b/defs.h
@@ -352,6 +352,24 @@ public:
   QString url_link_type_;
 };
 
+class UrlList : public QList<UrlLink>
+{
+public:
+  void AddUrlLink(const UrlLink& l)
+  {
+    push_back(l);
+  }
+
+  bool HasUrlLink() const
+  {
+    return !isEmpty();
+  }
+
+  const UrlLink& GetUrlLink() const
+  {
+    return first();
+  }
+};
 
 /*
  * Misc bitfields inside struct waypoint;
@@ -484,9 +502,7 @@ public:
    */
   QString notes;
 
-  /* TODO: UrlLink should probably move to a "real" class of its own.
-   */
-  QList<UrlLink> url_link_list_;
+  UrlList urls;
 
   wp_flags wpt_flags;
   QString icon_descr;
@@ -537,7 +553,7 @@ public:
 
   bool HasUrlLink() const;
   const UrlLink& GetUrlLink() const;
-  const QList<UrlLink> GetUrlLinks() const;
+  [[deprecated]] const QList<UrlLink> GetUrlLinks() const;
   void AddUrlLink(const UrlLink& l);
   QString CreationTimeXML() const;
   gpsbabel::DateTime GetCreationTime() const;
@@ -644,7 +660,7 @@ public:
   queue waypoint_list; /* List of child waypoints */
   QString rte_name;
   QString rte_desc;
-  QString rte_url;
+  UrlList rte_urls;
   int rte_num;
   int rte_waypt_ct;            /* # waypoints in waypoint list */
   format_specific_data* fs;
index 2c7fdb641f598ec772bd015c0965fc8c315a5434..8ac16c85fc87b392700e49b499dfeb87a3cf9b7f 100644 (file)
@@ -629,7 +629,11 @@ route_disp_hdr_cb(const route_head* rte)
   print_distance(cur_info->length, 0, 1, 0);
   print_course(cur_info->first_wpt, cur_info->last_wpt);
   gbfprintf(fout, "\t%d waypoints\t", cur_info->count);
-  print_string("%s\r\n", rte->rte_url);
+  if (rte->rte_urls.HasUrlLink()) {
+    print_string("%s\r\n", rte->rte_urls.GetUrlLink().url_);
+  } else {
+    print_string("%s\r\n", "");
+  }
   gbfprintf(fout, "\r\nHeader\t%s\r\n\r\n", headers[rtept_header]);
 }
 
@@ -681,7 +685,11 @@ track_disp_hdr_cb(const route_head* track)
   print_date_and_time(cur_info->time, 1);
   print_distance(cur_info->length, 0, 1, 0);
   print_speed(&cur_info->length, &cur_info->time);
-  print_string("%s", track->rte_url);
+  if (track->rte_urls.HasUrlLink()) {
+    print_string("%s", track->rte_urls.GetUrlLink().url_);
+  } else {
+    print_string("%s", "");
+  }
   gbfprintf(fout, "\r\n\r\nHeader\t%s\r\n\r\n", headers[trkpt_header]);
 }
 
@@ -1189,7 +1197,7 @@ parse_route_header()
       rte->rte_name = DUPSTR(str);
       break;
     case 5:
-      rte->rte_url = str;
+      rte->rte_urls.AddUrlLink(UrlLink(str));
       break;
     }
   }
@@ -1212,7 +1220,7 @@ parse_track_header()
       trk->rte_name = DUPSTR(str);
       break;
     case 6:
-      trk->rte_url = str;
+      trk->rte_urls.AddUrlLink(UrlLink(str));
       break;
     }
   }
diff --git a/gdb.cc b/gdb.cc
index 3268b033733e4a2459a725be3d32e2fcbb4a789d..41de965fcd5068c58ea6ddd54bbb21fecac907b7 100644 (file)
--- a/gdb.cc
+++ b/gdb.cc
@@ -901,9 +901,9 @@ read_route()
 
   /* VERSION DEPENDENT CODE */
   if (gdb_ver <= GDB_VER_2) {
-    rte->rte_url = fread_cstr();
+    rte->rte_urls.AddUrlLink(UrlLink(fread_cstr()));
   } else {
-    rte->rte_url = gdb_fread_strlist();
+    rte->rte_urls.AddUrlLink(UrlLink(gdb_fread_strlist()));
 
     int color_idx = FREAD_i32;
     rte->line_color.bbggrr = gt_color_value(color_idx);
@@ -971,9 +971,9 @@ read_track()
 
   /* VERSION DEPENDENT CODE */
   if (gdb_ver >= GDB_VER_3) {
-    res->rte_url = gdb_fread_strlist();
+    res->rte_urls.AddUrlLink(UrlLink(gdb_fread_strlist()));
   } else { /* if (gdb_ver <= GDB_VER_2) */
-    res->rte_url = FREAD_CSTR_AS_QSTR;
+    res->rte_urls.AddUrlLink(UrlLink(FREAD_CSTR_AS_QSTR));
   }
 #if GDB_DEBUG
   DBG(GDB_DBG_TRK, !res->rte_url.isNull())
@@ -1327,7 +1327,6 @@ write_waypoint(
     }
     FWRITE_CSTR(descr);
   } else { /* if (gdb_ver > GDB_VER_3) */
-    int cnt;
 //    url_link* url_next;
 //    const char* str;
     QString str;
@@ -1362,10 +1361,8 @@ write_waypoint(
     FWRITE_CSTR(str);                          /* instruction */
 #endif
 
-    cnt = 0;
-    cnt += wpt->url_link_list_.size();
-    FWRITE_i32(cnt);
-    foreach(UrlLink l, wpt->GetUrlLinks()) {
+    FWRITE_i32(wpt->urls.size());
+    foreach(UrlLink l, wpt->urls) {
       FWRITE_CSTR(l.url_);
     }
   }
@@ -1518,9 +1515,17 @@ write_route(const route_head* rte, const QString& rte_name)
 
   /* VERSION DEPENDENT CODE */
   if (gdb_ver <= GDB_VER_2) {
-    FWRITE_CSTR(rte->rte_url);
+    if (rte->rte_urls.HasUrlLink()) {
+      FWRITE_CSTR(rte->rte_urls.GetUrlLink().url_);
+    } else {
+      FWRITE_CSTR("");
+    }
   } else { /* if (gdb_ver >= GDB_VER_3) */
-    FWRITE_CSTR_LIST(rte->rte_url);
+    if (rte->rte_urls.HasUrlLink()) {
+      FWRITE_CSTR_LIST(rte->rte_urls.GetUrlLink().url_);
+    } else {
+      FWRITE_CSTR_LIST("");
+    }
     /* "Magenta" (14) is MapSource default */
     FWRITE_i32((rte->line_color.bbggrr < 0) ? 14 : gt_color_index_by_rgb(rte->line_color.bbggrr));
     FWRITE_C(0);
@@ -1560,9 +1565,17 @@ write_track(const route_head* trk, const QString& trk_name)
 
   /* VERSION DEPENDENT CODE */
   if (gdb_ver <= GDB_VER_2) {
-    FWRITE_CSTR(trk->rte_url);
+    if (trk->rte_urls.HasUrlLink()) {
+      FWRITE_CSTR(trk->rte_urls.GetUrlLink().url_);
+    } else {
+      FWRITE_CSTR("");
+    }
   } else { /* if (gdb_ver >= GDB_VER_3 */
-    FWRITE_CSTR_LIST(trk->rte_url);
+    if (trk->rte_urls.HasUrlLink()) {
+      FWRITE_CSTR_LIST(trk->rte_urls.GetUrlLink().url_);
+    } else {
+      FWRITE_CSTR_LIST("");
+    }
   }
 }
 
diff --git a/gpx.cc b/gpx.cc
index 43606dde01e929c49c8c9dab21f0c7db0f8a4ce3..41d1d3e4c2695454a3241a0916580c02b3eb4cb7 100644 (file)
--- a/gpx.cc
+++ b/gpx.cc
@@ -64,6 +64,7 @@ static QString current_tag;
 
 static Waypoint* wpt_tmp;
 static UrlLink* link_;
+static UrlLink* rh_link_;
 static bool cache_descr_is_html;
 static gpsbabel::File* iqfile;
 static gpsbabel::File* oqfile;
@@ -169,6 +170,11 @@ typedef enum  {
   tt_rte_name,
   tt_rte_desc,
   tt_rte_cmt,
+  tt_rte_url,          /* Not in GPX 1.1 */
+  tt_rte_urlname,      /* Not in GPX 1.1 */
+  tt_rte_link,         /* New in GPX 1.1 */
+  tt_rte_link_text,    /* New in GPX 1.1 */
+  tt_rte_link_type,    /* New in GPX 1.1 */
   tt_rte_number,
   tt_garmin_rte_display_color,
   tt_rte_rtept,
@@ -176,6 +182,11 @@ typedef enum  {
   tt_trk_desc,
   tt_trk_name,
   tt_trk_trkseg,
+  tt_trk_url,          /* Not in GPX 1.1 */
+  tt_trk_urlname,      /* Not in GPX 1.1 */
+  tt_trk_link,         /* New in GPX 1.1 */
+  tt_trk_link_text,    /* New in GPX 1.1 */
+  tt_trk_link_type,    /* New in GPX 1.1 */
   tt_trk_number,
   tt_garmin_trk_display_color,
   tt_trk_trkseg_trkpt,
@@ -208,7 +219,7 @@ struct GpxGlobal {
   QStringList url;
   QStringList urlname;
   QStringList keywords;
-  QList<UrlLink> link;
+  UrlList link;
   /* time and bounds aren't here; they're recomputed. */
 };
 static GpxGlobal* gpx_global = nullptr;
@@ -371,6 +382,11 @@ static tag_mapping tag_path_map[] = {
   { tt_rte, 0, "/gpx/rte" },
   { tt_rte_name, 0, "/gpx/rte/name" },
   { tt_rte_desc, 0, "/gpx/rte/desc" },
+  { tt_rte_url, 0, "/gpx/rte/url"},                            /* GPX 1.0 */
+  { tt_rte_urlname, 0, "/gpx/rte/urlname"},            /* GPX 1.0 */
+  { tt_rte_link, 0, "/gpx/rte/link"},                  /* GPX 1.1 */
+  { tt_rte_link_text, 0, "/gpx/rte/link/text"},        /* GPX 1.1 */
+  { tt_rte_link_type, 0, "/gpx/rte/link/type"},        /* GPX 1.1 */
   { tt_rte_number, 0, "/gpx/rte/number" },
   { tt_garmin_rte_display_color, 1, GARMIN_RTE_EXT "/gpxx:DisplayColor"},
 
@@ -380,6 +396,11 @@ static tag_mapping tag_path_map[] = {
   { tt_trk_name, 0, "/gpx/trk/name" },
   { tt_trk_desc, 0, "/gpx/trk/desc" },
   { tt_trk_trkseg, 0, "/gpx/trk/trkseg" },
+  { tt_trk_url, 0, "/gpx/trk/url"},                            /* GPX 1.0 */
+  { tt_trk_urlname, 0, "/gpx/trk/urlname"},            /* GPX 1.0 */
+  { tt_trk_link, 0, "/gpx/trk/link"},                  /* GPX 1.1 */
+  { tt_trk_link_text, 0, "/gpx/trk/link/text"},        /* GPX 1.1 */
+  { tt_trk_link_type, 0, "/gpx/trk/link/type"},        /* GPX 1.1 */
   { tt_trk_number, 0, "/gpx/trk/number" },
   { tt_garmin_trk_display_color, 1, GARMIN_TRK_EXT "/gpxx:DisplayColor"},
 
@@ -656,6 +677,7 @@ gpx_start(const QString& el, const QXmlStreamAttributes& attr)
   case tt_rte:
     rte_head = route_head_alloc();
     route_add_head(rte_head);
+    rh_link_ = new UrlLink;
     fs_ptr = &rte_head->fs;
     break;
   case tt_rte_rtept:
@@ -664,6 +686,7 @@ gpx_start(const QString& el, const QXmlStreamAttributes& attr)
   case tt_trk:
     trk_head = route_head_alloc();
     track_add_head(trk_head);
+    rh_link_ = new UrlLink;
     fs_ptr = &trk_head->fs;
     break;
   case tt_trk_trkseg_trkpt:
@@ -673,6 +696,12 @@ gpx_start(const QString& el, const QXmlStreamAttributes& attr)
       next_trkpt_is_new_seg = 0;
     }
     break;
+  case tt_rte_link:
+  case tt_trk_link:
+    if (attr.hasAttribute("href")) {
+      link_url = attr.value("href").toString();
+    }
+    break;
   case tt_unknown:
     start_something_else(el, attr);
     return;
@@ -900,11 +929,17 @@ gpx_end(const QString&)
     gpx_add_to_global(gpx_global->keywords, cdatastr);
     break;
   case tt_link:
-    (gpx_global->link).push_back(UrlLink(link_url, link_text, link_type));
+    (gpx_global->link).AddUrlLink(UrlLink(link_url, link_text, link_type));
     link_type.clear();
     link_text.clear();
     link_url.clear();
     break;
+  case tt_link_text:
+    link_text = cdatastr;
+    break;
+  case tt_link_type:
+    link_type = cdatastr;
+    break;
 
   /*
    * Waypoint-specific tags.
@@ -1010,6 +1045,13 @@ gpx_end(const QString&)
     rte_head->rte_name = cdatastr;
     break;
   case tt_rte:
+    if (rh_link_) {
+      if (!rh_link_->url_.isEmpty()) {
+        rte_head->rte_urls.AddUrlLink(*rh_link_);
+      }
+      delete rh_link_;
+      rh_link_ = nullptr;
+    }
     break;
   case tt_rte_rtept:
     if (link_) {
@@ -1028,6 +1070,12 @@ gpx_end(const QString&)
   case tt_garmin_rte_display_color:
     rte_head->line_color.bbggrr = gt_color_value_by_name(cdatastr);
     break;
+  case tt_rte_link:
+    rte_head->rte_urls.AddUrlLink(UrlLink(link_url, link_text, link_type));
+    link_type.clear();
+    link_text.clear();
+    link_url.clear();
+    break;
   case tt_rte_number:
     rte_head->rte_num = cdatastr.toInt();
     break;
@@ -1038,6 +1086,13 @@ gpx_end(const QString&)
     trk_head->rte_name = cdatastr;
     break;
   case tt_trk:
+    if (rh_link_) {
+      if (!rh_link_->url_.isEmpty()) {
+        trk_head->rte_urls.AddUrlLink(*rh_link_);
+      }
+      delete rh_link_;
+      rh_link_ = nullptr;
+    }
     break;
   case tt_trk_trkseg:
     next_trkpt_is_new_seg = 1;
@@ -1059,6 +1114,12 @@ gpx_end(const QString&)
   case tt_garmin_trk_display_color:
     trk_head->line_color.bbggrr = gt_color_value_by_name(cdatastr);
     break;
+  case tt_trk_link:
+    trk_head->rte_urls.AddUrlLink(UrlLink(link_url, link_text, link_type));
+    link_type.clear();
+    link_text.clear();
+    link_url.clear();
+    break;
   case tt_trk_number:
     trk_head->rte_num = cdatastr.toInt();
     break;
@@ -1078,6 +1139,22 @@ gpx_end(const QString&)
   /*
    * Items that are actually in multiple categories.
    */
+  case tt_rte_url:
+  case tt_trk_url:
+    rh_link_->url_ = cdatastr;
+    break;
+  case tt_rte_urlname:
+  case tt_trk_urlname:
+    rh_link_->url_link_text_ = cdatastr;
+    break;
+  case tt_rte_link_text:
+  case tt_trk_link_text:
+    link_text = cdatastr;
+    break;
+  case tt_rte_link_type:
+  case tt_trk_link_type:
+    link_type = cdatastr;
+    break;
   case tt_wpttype_ele:
     wpt_tmp->altitude = cdatastr.toDouble();
     break;
@@ -1144,12 +1221,6 @@ gpx_end(const QString&)
   case tt_wpttype_link_type:
     link_type = cdatastr;
     break;
-  case tt_link_text:
-    link_text = cdatastr;
-    break;
-  case tt_link_type:
-    link_type = cdatastr;
-    break;
   case tt_unknown:
     end_something_else();
     return;
@@ -1487,26 +1558,42 @@ void free_gpx_extras(xml_tag* tag)
 /*
  * Handle the grossness of GPX 1.0 vs. 1.1 handling of linky links.
  */
+static void
+write_gpx_url(const UrlList& urls)
+{
+  if (gpx_wversion_num > 10) {
+    for (const auto& l : urls) {
+      if (!l.url_.isEmpty()) {
+        writer->writeStartElement(QStringLiteral("link"));
+        writer->writeAttribute(QStringLiteral("href"), l.url_);
+        writer->writeOptionalTextElement(QStringLiteral("text"), l.url_link_text_);
+        writer->writeOptionalTextElement(QStringLiteral("type"), l.url_link_type_);
+        writer->writeEndElement();
+      }
+    }
+  } else {
+    UrlLink l = urls.GetUrlLink();
+    if (!l.url_.isEmpty()) {
+      writer->writeTextElement(QStringLiteral("url"), QString(urlbase) + l.url_);
+      writer->writeOptionalTextElement(QStringLiteral("urlname"), l.url_link_text_);
+    }
+  }
+}
+
 static void
 write_gpx_url(const Waypoint* waypointp)
 {
-  if (!waypointp->HasUrlLink()) {
-    return;
+  if (waypointp->HasUrlLink()) {
+    write_gpx_url(waypointp->urls);
   }
+}
 
-  if (gpx_wversion_num > 10) {
-    for (const auto& l : waypointp->GetUrlLinks()) {
-      writer->writeStartElement(QStringLiteral("link"));
-      writer->writeAttribute(QStringLiteral("href"), l.url_);
-      writer->writeOptionalTextElement(QStringLiteral("text"), l.url_link_text_);
-      writer->writeOptionalTextElement(QStringLiteral("type"), l.url_link_type_);
-      writer->writeEndElement();
-    }
-    return;
+static void
+write_gpx_url(const route_head* rh)
+{
+  if (rh->rte_urls.HasUrlLink()) {
+    write_gpx_url(rh->rte_urls);
   }
-  UrlLink l = waypointp->GetUrlLink();
-  writer->writeTextElement(QStringLiteral("url"), QString(urlbase) + l.url_);
-  writer->writeOptionalTextElement(QStringLiteral("urlname"), l.url_link_text_);
 }
 
 /*
@@ -1709,6 +1796,7 @@ gpx_track_hdr(const route_head* rte)
   writer->writeStartElement(QStringLiteral("trk"));
   writer->writeOptionalTextElement(QStringLiteral("name"), rte->rte_name);
   writer->writeOptionalTextElement(QStringLiteral("desc"), rte->rte_desc);
+  write_gpx_url(rte);
 
   if (rte->rte_num) {
     writer->writeTextElement(QStringLiteral("number"), QString::number(rte->rte_num));
@@ -1797,6 +1885,7 @@ gpx_route_hdr(const route_head* rte)
   writer->writeStartElement(QStringLiteral("rte"));
   writer->writeOptionalTextElement(QStringLiteral("name"), rte->rte_name);
   writer->writeOptionalTextElement(QStringLiteral("desc"), rte->rte_desc);
+  write_gpx_url(rte);
 
   if (rte->rte_num) {
     writer->writeTextElement(QStringLiteral("number"), QString::number(rte->rte_num));
index 442002256d84df662f1e9c896fe1b659cf1c14ff..00283b33525a461baab6cf931cbb6edf94583013 100644 (file)
@@ -85,7 +85,7 @@ read_tracks()
   trk_head->rte_num = 1;
   trk_head->rte_name = "PocketFMS";
   trk_head->rte_desc = "Breadcrumb";
-  trk_head->rte_url = "www.pocketfms.com";
+  trk_head->rte_urls.AddUrlLink(UrlLink("www.pocketfms.com"));
   track_add_head(trk_head);
 
   while (1 == gbfread(&bc, sizeof(bc), 1, file_in)) {
index 2e8f9622a60dbf70527f97b2d894db0971f7a188..dcfe680cb6e25ec959b892b06107a8edcd531961 100644 (file)
--- a/random.cc
+++ b/random.cc
@@ -131,7 +131,7 @@ random_read()
     }
     head->rte_desc = rand_qstr(16, nullptr);
        if RND(3) {
-      head->rte_url = rand_qstr(8, "http://rteurl.example.com/%s");
+      head->rte_urls.AddUrlLink(UrlLink(rand_qstr(8, "http://rteurl.example.com/%s")));
     }
   } else {
     head = nullptr;
index f35b3b94ad3c613dab4cf0fd490bb4e37f881459..30637b0ea80ca7053efe7dcd9512147a7f8e9e98 100644 (file)
@@ -5,6 +5,7 @@
   <trk>
     <name>PocketFMS</name>
     <desc>Breadcrumb</desc>
+    <url>www.pocketfms.com</url>
     <number>1</number>
     <trkseg>
       <trkpt lat="51.160198212" lon="14.946299553">
index af61a2d29e2f3343de31f8599a5ba46ed904a8d2..3707dfb66ea0706846b490f20907deabffe75461 100644 (file)
--- a/route.cc
+++ b/route.cc
@@ -366,7 +366,7 @@ route_copy(int* dst_count, int* dst_wpt_count, queue** dst, queue* src)
     route_head* rte_new = route_head_alloc();
     rte_new->rte_name = rte_old->rte_name;
     rte_new->rte_desc = rte_old->rte_desc;
-    rte_new->rte_url = rte_old->rte_url;
+    rte_new->rte_urls = rte_old->rte_urls;
     rte_new->fs = fs_chain_copy(rte_old->fs);
     rte_new->rte_num = rte_old->rte_num;
     any_route_add_head(rte_new, *dst);
index 2015b841f4e932b5295e0bbc7b897b9004895c8e..65dfab024a16868a64b880b1c605c0b9b1fc5375 100644 (file)
--- a/waypt.cc
+++ b/waypt.cc
@@ -381,13 +381,13 @@ waypt_restore(signed int count, queue* head_bak)
 void
 waypt_add_url(Waypoint* wpt, const QString& link, const QString& url_link_text)
 {
-  wpt->url_link_list_.push_back(UrlLink(link, url_link_text));
+  wpt->AddUrlLink(UrlLink(link, url_link_text));
 }
 
 void
 waypt_add_url(Waypoint* wpt, const QString& link, const QString& url_link_text, const QString& url_link_type)
 {
-  wpt->url_link_list_.push_back(UrlLink(link, url_link_text, url_link_type));
+  wpt->AddUrlLink(UrlLink(link, url_link_text, url_link_type));
 }
 
 double
@@ -606,7 +606,7 @@ Waypoint::Waypoint(const Waypoint& other) :
   shortname(other.shortname),
   description(other.description),
   notes(other.notes),
-  url_link_list_(other.url_link_list_),
+  urls(other.urls),
   wpt_flags(other.wpt_flags),
   icon_descr(other.icon_descr),
   creation_time(other.creation_time),
@@ -649,25 +649,25 @@ Waypoint::Waypoint(const Waypoint& other) :
 bool
 Waypoint::HasUrlLink() const
 {
-  return !url_link_list_.isEmpty();
+  return urls.HasUrlLink();
 }
 
 const UrlLink&
 Waypoint::GetUrlLink() const
 {
-  return url_link_list_[0];
+  return urls.GetUrlLink();
 }
 
-const QList<UrlLink>
+[[deprecated]] const QList<UrlLink>
 Waypoint::GetUrlLinks() const
 {
-  return url_link_list_;
+  return urls;
 }
 
 void
 Waypoint::AddUrlLink(const UrlLink& l)
 {
-  url_link_list_.push_back(l);
+  urls.AddUrlLink(l);
 }
 
 QString